require "#{RAILS_ROOT}/app/classes/cat/rules/rule"
require "#{RAILS_ROOT}/app/classes/cat/cat_item"

#* item_selection_rule.rb
#* Copyright (C) 2010  Embian Inc.
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or 
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with This program.  If not, see <http://www.gnu.org/licenses/>.
#  
#* Defines "Item Selection Rule" for CAT
#* Author: KyoungMo Yang(E-mail : mo@embian.com)
#* Modified: 2010-11-30
#* References: Rule, CATItem, Item, UserProfile
module ItemSelectionRule
  include Rule
  
  #* Functionailities
  # * Implements Rule#run
  #* Parameters 
  # * [*args] : argement list
  #* Return
  # * N/A
  #* Note
  # * Override Rule#run
  def run(*args)
    # raise "Invalid first argment that must be Float type as examinee's ability " unless args[0].is_a? Float
    # raise "Invalid second argment that must be nil or Array included CATItem list" unless args[1].is_a? Array
    return fishing_info
  end
  
  private
  #* Functionailities
  # * Implements "Fishing Information Rule" as Item Selection Rule of CAT
  #   Formula : I(Θ) = ( D^2 * a^2 * Q(Θ) * [P(Θ) - c]^2 ) / ([1 - c]^2 * P(Θ)), Lord, 1980
  #             I(Θ) = a^2 * Q(Θ) * P(Θ)
  #             P(Θ) = c + (1-c) * (1 / (1 + exp(-1.7*a*(Θ-b))))
  #             Q(Θ) = 1 - P(Θ)
  #* Parameters 
  # * N/A
  #* Return
  # * [irt_item] : Selected an instance of IRTItem
  #* Note
  # * N/A
  def fishing_info
    #TODO : item over-exposure problems
    #TODO : cutting point
    
    previous_items = profile.exam.items
    theta = profile.theta
    
    user = User.find(profile.user_id)
    exams = Exam.find(:all, :conditions=>{:user_id=>profile.user_id, :skill_id=>profile.classfication})
    exams.each do |exam|
      exam.exam_parts.each do |part|
        previous_items << CATItem.new(part.item.id.to_i, 0.0, 0.0, 0.0)
      end
    end
    
    exclusion_condition = _get_exclusion_query2(previous_items)
    item = Item.connection.select_one("SELECT id, answer, irt_a, irt_b, irt_c, P, (POW(irt_a, 2)* P * (1.0 - P)) as info 
                                        FROM (select id, answer, irt_a, irt_b, irt_c, (1.0 / (1.0 + EXP(-1.7*irt_a*(#{theta}-irt_b)))) as P from items where skill_id=#{profile.classfication}) as t 
                                        #{exclusion_condition} 
                                        ORDER BY info DESC")
    
    return item unless item
    cat_item = CATItem.new(item['id'].to_i, item['irt_a'].to_f, item['irt_b'].to_f, item['irt_c'].to_f)
    cat_item.answer = item['answer']
    return cat_item
  end
  
  private
  def _get_exclusion_query2(previous_items)
    return '' if previous_items.empty?
    query = []
    previous_items.each do |item|
      query << item.iid
    end
    query = query.join(' !=id and ')
    return "WHERE #{query} !=id " unless query.to_s.strip.empty?
  end
end